﻿using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace FastIM
{
    /// <summary>
    /// 消息类型，参照mqtt协议消息类型
    /// <seealso cref="https://blog.csdn.net/m0_57396609/article/details/119914301"/>
    /// <seealso cref="https://cloud.tencent.com/developer/news/271202"/>
    /// </summary>
    public enum MsgType
    {
        /// <summary>
        /// 请求连接
        /// </summary>
        Connect = 1,
        /// <summary>
        /// 请求应答
        /// </summary>
        ConnAck = 2,
        /// <summary>
        /// 发布消息（主题频道，频道群聊）
        /// </summary>
        Publish = 3,
        /// <summary>
        /// 发布应答
        /// </summary>
        PubAck = 4,
        /// <summary>
        /// 发布已接收，保证传递1
        /// </summary>
        PubRec = 5,
        /// <summary>
        /// 发布释放，保证传递2
        /// </summary>
        PubRel = 6,
        /// <summary>
        /// 发布完成，保证传递3
        /// </summary>
        PubComp = 7,
        /// <summary>
        /// 订阅请求
        /// </summary>
        Subscribe = 8,
        /// <summary>
        /// 订阅应答
        /// </summary>
        SubAck = 9,
        /// <summary>
        /// 取消订阅
        /// </summary>
        Unsubscribe = 10,
        /// <summary>
        /// 取消订阅应答
        /// </summary>
        UnsubAck = 11,
        /// <summary>
        /// ping请求
        /// </summary>
        PingReq = 12,
        /// <summary>
        /// ping响应
        /// </summary>
        PingResp = 13,
        /// <summary>
        /// 断开连接
        /// </summary>
        Disconnect = 14,
        Auth = 15,
        //以下是自定义消息
        /// <summary>
        /// 发布消息。发送一对一单聊消息
        /// </summary>
        PublishOne2One = 20,
        /// <summary>
        /// 发布消息。全员消息
        /// </summary>
        PublishOne2All = 21,
        /// <summary>
        /// 上线事件消息
        /// </summary>
        Online = 22,
        /// <summary>
        /// 下线事件消息
        /// </summary>
        Offline = 23
    }
    /// <summary>
    /// 消息质量，参照mqtt
    /// </summary>
    public enum QualityOfServiceLevel
    {
        AtMostOnce = 0x00,
        AtLeastOnce = 0x01,
        ExactlyOnce = 0x02
    }
    public enum RetainHandling
    {
        /// <summary>
        /// 服务器不保存数据，推送至当前订阅的就释放了。
        /// </summary>
        SendAtSubscribe = 0,
        /// <summary>
        /// 服务器持久化，如果有新的订阅者出现，就把这消息推送给它
        /// </summary>
        SendAtSubscribeIfNewSubscription = 1
    }
    /// <summary>
    /// （接收）消息模板
    /// <seealso cref="https://www.jianshu.com/p/b88d4ef5ac47"/>
    /// <seealso cref="https://zhuanlan.zhihu.com/p/112727752"/>
    /// <seealso cref="https://cloud.tencent.com/developer/news/271202"/>
    /// <seealso cref="https://zhuanlan.zhihu.com/p/429891532"/>
    /// </summary>
    public class MsgTemplate
    {
        /// <summary>
        /// 固定头部。消息命令类型
        /// </summary>
        public MsgType Type { get; set; }
        /// <summary>
        /// 接收消息体，不同的消息命令对应不同的消息体类型。onemsg消息体类型为OneMsgBody、channelmsg消息体类型为ChanneMsgBody、allmsg全员消息体类型为MsgBody、其他为string
        /// </summary>
        [JsonConverter(typeof(BodyConverter))]
        public MsgBody Body { get; set; }
    }
    /// <summary>
    /// 自定义布尔类型数据转换规则
    /// <seealso cref="https://blog.csdn.net/qq_45534034/article/details/114636587"/>
    /// </summary>
    public class BodyConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType) => typeof(MsgBody).IsAssignableFrom(objectType);
        //在序列化对象时，就使用 Newton.Json 默认的 WriteJson 而不是使用 BodyConverter 中重写的 WriteJson。
        public override bool CanWrite { get { return false; } }
        //反序列化
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            try
            {
                //var jsonObject = JObject.Load(reader);
                var target = new RequestMsgBody();
                serializer.Populate(reader, target);
                return target;
            }
            catch (Exception ex)
            {
                throw new Exception("BodyConverter解析异常：" + ex.Message);
            }
        }
        //序列化
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            //writer.WriteValue(value);
        }
    }

    /// <summary>
    /// 客户端（接收/发送）消息体
    /// </summary>
    public abstract class MsgBody
    {
        /// <summary>
        /// 可变头【发送端传值，接收端接收值】消息id，发送端自定义发送消息id。用于发送消息接收端是否已阅回传标识
        /// </summary>
        public string MsgId { get; set; }
        /// <summary>
        /// 【发送端传值，接收端接收值】负载消息内容数据。（PayLoad）在可变头的后面，包含CONNECT、SUBSCRIBE、SUBACK、UNSUBSCRIBE四种类型的消息：
        /// </summary>
        public object? Payload { get; set; } = null;
        /// <summary>
        /// 【发送端传值，接收端接收值】群聊频道（可选）。发送群聊频道消息类型需要
        /// </summary>
        public string? Channel { get; set; } = null;
        /// <summary>
        /// 【发送端传值,接收端接收值】发送者登录相关信息（可以传递给接收者的信息）
        /// </summary>
        public ClientMetaData? SenderMetaData { get; set; } = null;
    }
    /// <summary>
    /// 接收请求消息体
    /// </summary>
    public class RequestMsgBody : MsgBody
    {
        /// <summary>
        /// 固定头部。主要用于PUBLISH（发布态）消息的，保证消息传递的次数。00表示最多一次 即01表示至少一次 即>=110表示一次，即==111保留后用
        /// </summary>
        public QualityOfServiceLevel Qos { get; set; } = 0;
        /// <summary>
        /// 固定头部，服务器是否持久化消息。主要用于PUBLISH(发布态)的消息，表示服务器要保留这次推送的信息，如果有新的订阅者出现，就把这消息推送给它。如果不设那么推送至当前订阅的就释放了
        /// </summary>
        public RetainHandling Retain { get; set; } = 0;
        /// <summary>
        /// 【发送端传值，服务端接收值】消息接收客户端id。一对一单聊消息类型需要。(消息发送端需要携带此消息，接收端接收信息不必有此消息)
        /// </summary>
        //public string ReceiveClientId { get; set; } = null;
        public List<string> ReceiveClientId { get; set; } = new List<string>();
    }
    /*
    public class ReceiveClientIdConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType) => true;
        //在序列化对象时，就使用 Newton.Json 默认的 WriteJson 而不是使用 BodyConverter 中重写的 WriteJson。
        public override bool CanWrite { get { return false; } }
        //反序列化
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            var it = (long)reader.Value;
            DateTime dateTimeStart = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1));
            long lTime = it * 10000;
            TimeSpan nowTimeSpan = new TimeSpan(lTime);
            DateTime resultDateTime = dateTimeStart.Add(nowTimeSpan);
            return resultDateTime;
        }
        //序列化
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            //writer.WriteValue(value);
        }
    }
    */
    /// <summary>
    /// 响应消息体
    /// </summary>
    public class ResponseMsgBody : MsgBody
    {
        /// <summary>
        /// 【服务器传值】消息发送客户端id(消息发送端不需要携带此消息，接收端接收信息必须有此消息)
        /// </summary>
        public string SenderClientId { get; set; } = null;
        /// <summary>
        /// 【服务器传值】服务器相应状态码
        /// </summary>
        public int Code { get; set; }
    }
    /// <summary>
    /// 客户端登录相关信息
    /// </summary>
    public class ClientMetaData
    {
        /// <summary>
        /// 设备名称
        /// </summary>
        public string DeviceName { get; set; } = "PC";
        /// <summary>
        /// 客户端IP
        /// </summary>
        public string? IP { get; set; }
        /// <summary>
        /// 头像地址
        /// </summary>
        public string? Photo { get; set; }
        /// <summary>
        /// 其他相关信息，比如头像等，客户端定义
        /// </summary>
        public object? Other { get; set; }
    }
}
